/*
 * Copyright (c) 2021 柯华栋
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ipc_server.h"

// connection和id关联相关 -----------------------------------------------------------------

typedef struct
{
	gchar* 				id;
	int 			 	size;
	int 			 	capacity;
	GDBusConnection** 	data;
} connections_t;

typedef struct
{
	GDBusConnection*	connection;
	int 				size;
	int 				capacity;
	gchar** 			data;
} ids_t;

typedef struct {
	int 			size;
	int 			capacity;
	connections_t** items;
} id_map_t;

typedef struct {
	int 	size;
	int 	capacity;
	ids_t** items;
} connection_map_t;

static id_map_t 		s_id_map;
static connection_map_t s_connection_map;

#define get_new_capcity_by_size(x) (((x) + 16) * 3 / 2)

// ---------------------------------------------------------------

static connections_t* connections_new(const gchar* id)
{
	connections_t* item = (connections_t*)malloc(sizeof(connections_t));
	assert(item);
	item->id = str_check_dup(id);
	item->size = 0;
	item->capacity = get_new_capcity_by_size(0);
	item->data = (GDBusConnection**) malloc(sizeof(GDBusConnection*) * item->capacity);
	return item;
}

static void connections_free(connections_t* item)
{
	str_check_free(item->id);
	g_free(item->data);
	g_free(item);
}

static int connections_get_pos(connections_t* item, GDBusConnection* connection)
{
	int first = 0;
    int last = item->size;

    while (last > first) {
        int next = (last + first) >> 1;
        int cmp = (int)((void*)connection - (void*)(item->data[next]));
        if (!cmp)
            return -next - 1;
        if (cmp < 0) {
            last = next;
            continue;
        }
        first = next + 1;
    }
    return first;
}

static void connections_insert_pos(connections_t* item, int pos, GDBusConnection* connection)
{
	assert(pos >= 0);
	// 空间满，重新分配
	if (item->size == item->capacity) {
		item->capacity = get_new_capcity_by_size(item->size);
		item->data = (GDBusConnection**) realloc(item->data, sizeof(GDBusConnection*) * item->capacity);
	}

	item->size++;
	// 不添加在末端, 留出位置
	if (item->size > pos) {
		memmove(item->data + pos + 1, item->data + pos, sizeof(GDBusConnection*) * (item->size - pos - 1));
	}
	item->data[pos] = connection;
}


static void connections_remove_pos(connections_t* item, int pos)
{
	assert(pos < 0);
	pos = -pos - 1;
	item->data[pos] = NULL;
	item->size--;
	if (pos < item->size) {
		memmove(item->data + pos, item->data + pos + 1, sizeof(GDBusConnection*) * (item->size - pos));
	}
}

// ---------------------------------------------------------------

static ids_t* ids_new(GDBusConnection* connection)
{
	ids_t* item = (ids_t*)malloc(sizeof(ids_t));
	assert(item);
	item->connection = connection;
	item->size = 0;
	item->capacity = get_new_capcity_by_size(0);
	item->data = (gchar**) malloc(sizeof(gchar*) * item->capacity);
	return item;
}

static void ids_free(ids_t* item)
{
	for (int i = 0; i < item->size; i++)
		g_free(item->data[i]);
	free(item->data);
	free(item);
}

static int ids_get_pos(ids_t* item, const gchar* id)
{
	int first = 0;
    int last = item->size;

    while (last > first) {
        int next = (last + first) >> 1;
        int cmp = strcmp(id, item->data[next]);
        if (!cmp)
            return -next - 1;
        if (cmp < 0) {
            last = next;
            continue;
        }
        first = next + 1;
    }
    return first;
}

static void ids_insert_pos(ids_t* item, int pos, const gchar* id)
{
	assert(pos >= 0);
	// 空间满，重新分配
	if (item->size == item->capacity) {
		item->capacity = get_new_capcity_by_size(item->size);
		item->data = (gchar**) realloc(item->data, sizeof(gchar*) * item->capacity);
	}

	item->size++;
	// 不添加在末端, 留出位置
	if (item->size > pos) {
		memmove(item->data + pos + 1, item->data + pos, sizeof(gchar*) * (item->size - pos - 1));
	}
	item->data[pos] = str_check_dup(id);
}


static void ids_remove_pos(ids_t* item, int pos)
{
	assert(pos < 0);
	pos = -pos - 1;
	str_check_free(item->data[pos]);
	item->data[pos] = NULL;
	item->size--;
	if (pos < item->size) {
		memmove(item->data + pos, item->data + pos + 1, sizeof(gchar*) * (item->size - pos));
	}
}


// ---------------------------------------------------------------

static int id_map_get_pos(const gchar* id)
{
	int first = 0;
    int last = s_id_map.size;

    while (last > first) {
        int next = (last + first) >> 1;
        int cmp = strcmp(id, s_id_map.items[next]->id);
        if (!cmp)
            return -next - 1;
        if (cmp < 0) {
            last = next;
            continue;
        }
        first = next + 1;
    }
    return first;
}


static void id_map_insert_pos(int pos, connections_t* item)
{
	assert(pos >= 0);

	// 空间满，重新分配
	if (s_id_map.size == s_id_map.capacity) {
		s_id_map.capacity = get_new_capcity_by_size(s_id_map.size);
		s_id_map.items = (connections_t**) realloc(s_id_map.items,
			sizeof(connections_t*) * s_id_map.capacity);
	}

	s_id_map.size++;
	// 不添加在末端, 留出位置
	if (s_id_map.size > pos) {
		memmove(s_id_map.items + pos + 1, s_id_map.items + pos,
			sizeof(connections_t*) * (s_id_map.size - pos - 1));
	}
	s_id_map.items[pos] = item;
}

static void id_map_remove_pos(int pos)
{
	assert(pos < 0);
	pos = -pos - 1;
	connections_free(s_id_map.items[pos]);
	s_id_map.items[pos] = NULL;
	s_id_map.size--;
	if (pos < s_id_map.size) {
		memmove(s_id_map.items + pos, s_id_map.items + pos + 1,
			sizeof(connections_t*) * (s_id_map.size - pos));
	}
}

// ---------------------------------------------------------------


static int connection_map_get_pos(GDBusConnection* connection)
{
	int first = 0;
    int last = s_connection_map.size;

    while (last > first) {
        int next = (last + first) >> 1;
        int cmp = (int)((void*)connection - (void*)(s_connection_map.items[next]->connection));
        if (!cmp)
            return -next - 1;
        if (cmp < 0) {
            last = next;
            continue;
        }
        first = next + 1;
    }
    return first;
}


static void connection_map_insert_pos(int pos, ids_t* item)
{
	assert(pos >= 0);

	// 空间满，重新分配
	if (s_connection_map.size == s_connection_map.capacity) {
		s_connection_map.capacity = get_new_capcity_by_size(s_connection_map.size);
		s_connection_map.items = (ids_t**) realloc(s_connection_map.items,
			sizeof(ids_t*) * s_connection_map.capacity);
	}

	s_connection_map.size++;
	// 不添加在末端, 留出位置
	if (s_connection_map.size > pos) {
		memmove(s_connection_map.items + pos + 1, s_connection_map.items + pos,
			sizeof(ids_t*) * (s_connection_map.size - pos - 1));
	}
	s_connection_map.items[pos] = item;
}


static void connection_map_remove_pos(int pos)
{
	assert(pos < 0);
	pos = -pos - 1;
	ids_free(s_connection_map.items[pos]);
	s_connection_map.items[pos] = NULL;
	s_connection_map.size--;
	if (pos < s_connection_map.size) {
		memmove(s_connection_map.items + pos, s_connection_map.items + pos + 1,
			sizeof(ids_t*) * (s_connection_map.size - pos));
	}
}



// ---------------------------------------------------------------

static void add_id(GDBusConnection* connection, const gchar* id)
{
	ipc_lock();

	ids_t* ids = NULL;
	int ids_pos = connection_map_get_pos(connection);

	int id_pos;
	if (ids_pos >= 0) {
		ids = ids_new(connection);
		connection_map_insert_pos(ids_pos, ids);
		id_pos = 0;
	} else {
		ids_pos = -ids_pos-1;
		ids = s_connection_map.items[ids_pos];
		id_pos = ids_get_pos(ids, id);
	}

	if (id_pos < 0) {
		ipc_unlock();
		return;
	}
	ids_insert_pos(ids, id_pos, id);

	// connection插入 id->connections的connections

	connections_t* connections = NULL;
	int connections_pos = id_map_get_pos(id);

	// id->connections不存在, 创建connections, 并建立id->connections
	if (connections_pos >= 0) {
		connections = connections_new(id);
		id_map_insert_pos(connections_pos, connections);
		connections_insert_pos(connections, 0, connection);
	}
	else {
		connections_pos = -connections_pos-1;
		connections = s_id_map.items[connections_pos];
		int connection_pos = connections_get_pos(connections, connection);
		assert(connection_pos >= 0);
		connections_insert_pos(connections, connection_pos, connection);
	}

	ipc_unlock();
}

static void remove_id(GDBusConnection* connection, const gchar* id)
{
	ipc_lock();

	// id->connections不存在
	int connections_pos = id_map_get_pos(id);
	if (connections_pos >= 0) {
		ipc_unlock();
		return;
	}
	
	// connections没有connection
	connections_t* connections = s_id_map.items[-connections_pos-1];
	int connection_pos = connections_get_pos(connections, connection);
	if (connection_pos >= 0) {
		ipc_unlock();
		return;
	}	
	
	// id->connections的connections中移除conneciton
	connections_remove_pos(connections, connection_pos);
	
	// connections为空, id->connections断开
	if (connections->size == 0) {
		id_map_remove_pos(connections_pos);
	}
	
	int ids_pos = connection_map_get_pos(connection);
	assert(ids_pos < 0);
	
	ids_t* ids = s_connection_map.items[-ids_pos-1];
	
	int id_pos = ids_get_pos(ids, id);
	assert(id_pos < 0);
	
	// connection->ids的ids中移除id
	ids_remove_pos(ids, id_pos);
	
	// ids为空, connection->ids断开
	if (ids->size == 0) {
		connection_map_remove_pos(ids_pos);
	}

	ipc_unlock();
}

static void clear_ids(GDBusConnection* connection)
{
	ipc_lock();
	
	int ids_pos = connection_map_get_pos(connection);
	// 没有connection -> ids
	if (ids_pos >= 0) {
		ipc_unlock();
		return;
	}
	
	ids_t* ids = s_connection_map.items[-ids_pos-1];
	for (int i = 0; i < ids->size; i++) {		
		int connections_pos = id_map_get_pos(ids->data[i]);
		assert(connections_pos < 0);
		
		connections_t* connections = s_id_map.items[-connections_pos-1];
		
		int connection_pos = connections_get_pos(connections, connection);
		assert(connection_pos < 0);
		
		// id -> connections的connections中移除connection
		connections_remove_pos(connections, connection_pos);
		
		// connections为空 断开id -> connections
		if (connections->size == 0) {
			id_map_remove_pos(connections_pos);
		}
	}
	
	// ids为空， 断开connection -> ids
	connection_map_remove_pos(ids_pos);
	
	ipc_unlock();
}


// 控制器相关 ---------------------------------------------------------------------

static module_controller_getter s_module_controller_getter;

/** 设置控制器工厂方法 */
void server_set_module_controller_getter(module_controller_getter getter)
{
	s_module_controller_getter = getter;
}

/** 通过模块名获取控制器 */
static module_controller_t* module_get_controller_by_name(const gchar* module_name)
{
	module_controller_getter getter = NULL;
	while ((getter = s_module_controller_getter) == NULL) {
		// 等到10毫秒
		usleep(10*1000);
	}
	return getter(module_name);
}

static gchar* get_module_name(const gchar* id, gchar* buffer, int size)
{
	if (id == NULL) {
		return NULL;
	}
	
	const gchar* name_after = strchr(id, '.');
	int len = name_after-id;
	if (name_after == NULL || len == 0) {
		return NULL;
	}
	
	if (len < size) {
		memcpy(buffer, id, len);
		buffer[len] = 0;
		return buffer;
	}
	return g_strndup(id, name_after-id);
}

/** 通过id获取控制器 */
static module_controller_t* module_get_controller_by_id(const gchar* id)
{
	gchar buffer[64];	// 避免频繁的创建和释放	
	gchar* module_name = get_module_name(id, buffer, 64);
	if (module_name == NULL) {
		return NULL;
	}
	
	module_controller_t* controller = module_get_controller_by_name(module_name);
	
	if (module_name != buffer) {
		g_free(module_name);
	}
	
	return controller;
}

/** 通过map获取控制器 */
static module_controller_t* module_get_controller_by_map(ipc_map_t* map)
{
	const gchar* id = (map == NULL ? NULL : ipc_map_get_string(map, IPC_MAP_KEY_ID));
	return module_get_controller_by_id(id);
}



// 回调相关 --------------------------------------------------------------------


static void on_post(ipc_map_t* map)
{
	// 转发给controller处理
	controller_on_post on_post = NULL;
	module_controller_t* controller = module_get_controller_by_map(map);
	if (controller != NULL) {
		on_post = controller->on_post;
	}
	if (on_post != NULL) {
		on_post(map);
	}
}


static ipc_map_t* on_send(ipc_map_t* map)
{
	// 转发给controller处理
	controller_on_send on_send = NULL;
	module_controller_t* controller = module_get_controller_by_map(map);
	if (controller != NULL) {
		on_send = controller->on_send;
	}
	if (on_send != NULL) {
		return on_send(map);
	}
	return NULL;
}


static void on_add_id(GDBusConnection* connection, const gchar* id, gint64 observer)
{
	// 添加connection和id的关联
	add_id(connection, id);

	// 转发给controller处理
	controller_handle_update handle_update = NULL;
	module_controller_t* controller = module_get_controller_by_id(id);
	if (controller != NULL) {
		handle_update = controller->handle_update;
	}
	if (handle_update != NULL) {
		handle_update(connection, id, observer);
	}
}


static void on_remove_id(GDBusConnection* connection, const gchar* id)
{
	// 取消connection和id的关联
	remove_id(connection, id);

	// TEST 打印
	g_print("\n\t\tON_REMOVE_ID\n");
	g_print("on_remove_id %s\n", id);
}

/* 函数回调 */
static void on_callback(GDBusConnection* connection,
	const gchar* sender, const gchar* object_path, const gchar*	interface_name,
	const gchar* method_name,
	GVariant* parameters, GDBusMethodInvocation* invocation, gpointer user_data)
{
	// g_print("on_callback %p\n", connection);
	
	if (g_strcmp0(method_name, "post") == 0) {
		ipc_map_t* map = ipc_map_from_variant(parameters);
		if (map != NULL) {
			// 处理参数
			on_post(map);

			// 释放参数
			ipc_map_unref(map);
		}
		return;
	}

	if (g_strcmp0(method_name, "send") == 0) {
		ipc_map_t* map = ipc_map_from_variant(parameters);
		ipc_map_t* ret = NULL;
		if (map != NULL) {
			// 处理参数
			ret = on_send(map);

			// 释放参数
			if (ret != map) {
				ipc_map_unref(map);
			}
		}

		// 返回结果给客户端
		g_dbus_method_invocation_return_value(invocation, ipc_map_to_variant(ret));

		// 释放返回值
		if (ret != NULL) {
			ipc_map_unref(ret);
		}
		return;
	}

	if (g_strcmp0(method_name, "add_id") == 0) {
		// 获取参数
		GVariant* data = NULL;
		gchar* id = NULL;
		guint64 observer = 0;
		g_variant_get(parameters, "(@(sx))", &data);
		g_variant_get(data, "(sx)", &id, &observer);

		if (id != NULL) {
			// 处理参数
			on_add_id(connection, id, observer);

			// 释放参数
			g_free(id);
		}
		return;
	}

	if (g_strcmp0(method_name, "remove_id") == 0) {
		// 获取参数
		gchar* id = NULL;
		g_variant_get(parameters, "(s)", &id);

		if (id != NULL) {
			// 处理参数
			on_remove_id(connection, id);

			// 释放参数
			g_free(id);
		}
		return;
	}
}

/** 接口表 */
static const GDBusInterfaceVTable interface_vtable =
{
	on_callback,
	NULL,
	NULL,
};


/* 连接关闭 */
static void connection_closed(GDBusConnection* connection,
	gboolean remote_peer_vanished, GError* Error, gpointer	user_data)
{
	// g_print("connection_closed %p\n", connection);

	// 取消所有和connection关联的id和connection的关联
	clear_ids(connection);

	// 取消引用
	g_object_unref(connection);
}


static const gchar k_node_info_xml[] =
"<node>"
"  <interface name='com.car.ipc.Channel'>"
"    <method name='post'>"
"      <arg type='ay' name='data' direction='in'/>"
"    </method>"
"    <method name='send'>"
"      <arg type='ay' name='data' direction='in'/>"
"      <arg type='ay' name='ret' direction='out'/>"
"    </method>"
"    <method name='add_id'>"
"      <arg type='(sx)' name='ids' direction='in'/>"
"    </method>"
"    <method name='remove_id'>"
"      <arg type='s' name='id' direction='in'/>"
"    </method>"
"  </interface>"
"</node>";


/* 新连接 */
static gboolean on_new_connection(GDBusServer* server,
	GDBusConnection* connection, gpointer user_data)
{
	// g_print("on_new_connection %p\n", connection);

	// 节点信息
	static GDBusNodeInfo* node_info = NULL;
	if (node_info == NULL)
		node_info = g_dbus_node_info_new_for_xml(k_node_info_xml, NULL);

	// 引用
	g_object_ref(connection);

	// 监听断开连接
	g_signal_connect(connection, "closed", G_CALLBACK(connection_closed), NULL);

	// 注册接口虚表
	guint id = g_dbus_connection_register_object(
		connection,
		"/com/car/ipc/Channel",
		node_info->interfaces[0],
		&interface_vtable,
		NULL, 	/* user_data */
		NULL, 	/* user_data_free_func */
		NULL	/* GError */
	);

	g_assert(id > 0);

	return TRUE;
}


// ----------------------------------------------------------------------------------

/** 创建服务端 */
GDBusServer* server_new(const gchar* server_name)
{
	// 地址
	gchar* address = g_strdup_printf("unix:abstract=%s", server_name);

	// 唯一ID
	gchar* guid = g_dbus_generate_guid();

	// 错误信息
	GError* error = NULL;

	// 创建服务端
	GDBusServer* server = g_dbus_server_new_sync(address,
		G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS,
		guid, NULL, NULL, &error);

	// 创建服务端失败
	if (server == NULL) {
		// 打印错误信息
		g_printerr("Error creating server at address %s: %s\n", address, error->message);
		g_error_free(error);
	} else {
		// 监听连接
		g_signal_connect(server, "new-connection", G_CALLBACK(on_new_connection), NULL);

		// 启动服务
		g_dbus_server_start(server);
	}

	// 释放地址
	g_free(address);

	// 释放唯一ID
	g_free(guid);

	return server;
}


controller_on_post id_pointer_map_get_on_post(id_pointer_map_t* id_pointer_map, ipc_map_t* map)
{
	return (controller_on_post)id_pointer_map_get(id_pointer_map, module_get_id_by_map(map));
}

controller_on_send id_pointer_map_get_on_send(id_pointer_map_t* id_pointer_map, ipc_map_t* map)
{
	return (controller_on_send)id_pointer_map_get(id_pointer_map, module_get_id_by_map(map));
}

controller_handle_update id_pointer_map_get_handle_update(id_pointer_map_t* id_pointer_map, const gchar* id)
{
	return (controller_handle_update)id_pointer_map_get(id_pointer_map, id);
}


// update给客户端相关  ----------------------------------------------------------------------


/* (用于服务端对多个connection进行update时, 避免多次map序列化为data) */
static inline void connection_post_variant(GDBusConnection* connection, GVariant* data)
{
	g_dbus_connection_call(connection,
		NULL, "/com/car/ipc/Channel", "com.car.ipc.Channel", "post",
		data, NULL,
		G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}


// 更新给指定connection
void server_update_con(GDBusConnection* connection, ipc_map_t* map)
{
	if (connection == NULL || map == NULL) {
		return;
	}
	
	ipc_lock();
	
	const gchar* id = ipc_map_get_string(map, IPC_MAP_KEY_ID);
	if (id == NULL || id[0] == 0) {
		goto finish;
	}
	
	// 更新给指定conneciton
	connection_post_variant(connection, ipc_map_to_variant(map));
	
finish:
	ipc_map_unref(map);
	ipc_unlock();
}

// 更新给和id关联的所有connection
void server_update(ipc_map_t* map)
{
	if (map == NULL) {
		return;
	}
	
	ipc_lock();
	
	const gchar* id = ipc_map_get_string(map, IPC_MAP_KEY_ID);
	if (id == NULL || id[0] == 0) {
		goto finish;
	}
	
	int connections_pos = id_map_get_pos(id);
	if (connections_pos >= 0) {
		goto finish;
	}
	
	connections_t* connections = s_id_map.items[-connections_pos-1];
	if (connections->size == 0) {
		goto finish;
	}
	
	// 更新给和id关联的所有connection
	GVariant* data = ipc_map_to_variant(map);
	for (int i = 0; i < connections->size; i++) {
		g_variant_ref(data);
		connection_post_variant(connections->data[i], data);
	}
	g_variant_unref(data);
	
finish:
	ipc_map_unref(map);
	ipc_unlock();
}

// --------------------------------------------------------------------------------------------------------------------

void server_update_u8(const gchar* id, uint8_t value)
{
	server_update(ipc_map_new_id_u8(id, value));
}

void server_update_s16(const gchar* id, int16_t value)
{
	server_update(ipc_map_new_id_s16(id, value));
}

void server_update_s32(const gchar* id, int32_t value)
{
	server_update(ipc_map_new_id_s32(id, value));
}

void server_update_s64(const gchar* id, int64_t value)
{
	server_update(ipc_map_new_id_s64(id, value));
}

void server_update_float(const gchar* id, float value)
{
	server_update(ipc_map_new_id_float(id, value));
}

void server_update_double(const gchar* id, double value)
{
	server_update(ipc_map_new_id_double(id, value));
}

void server_update_string(const gchar* id, const gchar* value)
{
	server_update(ipc_map_new_id_string(id, value));
}


void server_update_u8_array(const gchar* id, ipc_u8_array_t* array)
{
	server_update(ipc_map_new_id_u8_array(id, array));
}

void server_update_s16_array(const gchar* id, ipc_s16_array_t* array)
{
	server_update(ipc_map_new_id_s16_array(id, array));
}

void server_update_s32_array(const gchar* id, ipc_s32_array_t* array)
{
	server_update(ipc_map_new_id_s32_array(id, array));
}

void server_update_s64_array(const gchar* id, ipc_s64_array_t* array)
{
	server_update(ipc_map_new_id_s64_array(id, array));
}

void server_update_float_array(const gchar* id, ipc_float_array_t* array)
{
	server_update(ipc_map_new_id_float_array(id, array));
}

void server_update_double_array(const gchar* id, ipc_double_array_t* array)
{
	server_update(ipc_map_new_id_double_array(id, array));
}

void server_update_string_array(const gchar* id, ipc_string_array_t* array)
{
	server_update(ipc_map_new_id_string_array(id, array));
}

// -----------------------------------------------------------------

void server_update_con_observer_u8(GDBusConnection* connection, const gchar* id, gint64 observer, uint8_t value)
{
	server_update_con(connection, ipc_map_new_id_observer_u8(id, observer, value));
}

void server_update_con_observer_s16(GDBusConnection* connection, const gchar* id, gint64 observer, int16_t value)
{
	server_update_con(connection, ipc_map_new_id_observer_s16(id, observer, value));
}

void server_update_con_observer_s32(GDBusConnection* connection, const gchar* id, gint64 observer, int32_t value)
{
	server_update_con(connection, ipc_map_new_id_observer_s32(id, observer, value));
}

void server_update_con_observer_s64(GDBusConnection* connection, const gchar* id, gint64 observer, int64_t value)
{
	server_update_con(connection, ipc_map_new_id_observer_s64(id, observer, value));
}

void server_update_con_observer_float(GDBusConnection* connection, const gchar* id, gint64 observer, float value)
{
	server_update_con(connection, ipc_map_new_id_observer_float(id, observer, value));
}

void server_update_con_observer_double(GDBusConnection* connection, const gchar* id, gint64 observer, double value)
{
	server_update_con(connection, ipc_map_new_id_observer_double(id, observer, value));
}

void server_update_con_observer_string(GDBusConnection* connection, const gchar* id, gint64 observer, const gchar* value)
{
	server_update_con(connection, ipc_map_new_id_observer_string(id, observer, value));
}



void server_update_con_observer_u8_array(GDBusConnection* connection, const gchar* id, gint64 observer, ipc_u8_array_t* array)
{
	server_update_con(connection, ipc_map_new_id_observer_u8_array(id, observer, array));
}

void server_update_con_observer_s16_array(GDBusConnection* connection, const gchar* id, gint64 observer, ipc_s16_array_t* array)
{
	server_update_con(connection, ipc_map_new_id_observer_s16_array(id, observer, array));
}

void server_update_con_observer_s32_array(GDBusConnection* connection, const gchar* id, gint64 observer, ipc_s32_array_t* array)
{
	server_update_con(connection, ipc_map_new_id_observer_s32_array(id, observer, array));
}

void server_update_con_observer_s64_array(GDBusConnection* connection, const gchar* id, gint64 observer, ipc_s64_array_t* array)
{
	server_update_con(connection, ipc_map_new_id_observer_s64_array(id, observer, array));
}

void server_update_con_observer_float_array(GDBusConnection* connection, const gchar* id, gint64 observer, ipc_float_array_t* array)
{
	server_update_con(connection, ipc_map_new_id_observer_float_array(id, observer, array));
}

void server_update_con_observer_double_array(GDBusConnection* connection, const gchar* id, gint64 observer, ipc_double_array_t* array)
{
	server_update_con(connection, ipc_map_new_id_observer_double_array(id, observer, array));
}

void server_update_con_observer_string_array(GDBusConnection* connection, const gchar* id, gint64 observer, ipc_string_array_t* array)
{
	server_update_con(connection, ipc_map_new_id_observer_string_array(id, observer, array));
}

